/*
 * @[H]:  Copyright (c) 2021 Phytium Information Technology, Inc. 
 * 
 *  SPDX-License-Identifier: Apache-2.0. 
 * 
 * @Date: 2021-07-19 21:48:43
 * @LastEditTime: 2021-07-23 20:32:53
 * @Description:  Description of file
 * @Modify History: 
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */

#include "f_xmac.h"
#include "ft_types.h"
#include "f_xmac_hw.h"

/*****************************************************************************/
/**
* Start the Ethernet controller as follows:
*   - Enable transmitter if XTE_TRANSMIT_ENABLE_OPTION is set
*   - Enable receiver if XTE_RECEIVER_ENABLE_OPTION is set
*   - Start the SG DMA send and receive channels and enable the device
*     interrupt
*
* @param instance_p is a pointer to the instance to be worked on.
*
* @return N/A
*
* @note
* Hardware is configured with scatter-gather DMA, the driver expects to start
* the scatter-gather channels and expects that the user has previously set up
* the buffer descriptor lists.
*
* This function makes use of internal resources that are shared between the
* Start, Stop, and Set/ClearOptions functions. So if one task might be setting
* device options while another is trying to start the device, the user is
* required to provide protection of this shared data (typically using a
* semaphore).
*
* This function must not be preempted by an interrupt that may service the
* device.
*
******************************************************************************/
void FXmacStart(FXmac *instance_p)
{
    u32 Reg;

    /* Assert bad arguments and conditions */
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY);

    /* Start DMA */
    /* When starting the DMA channels, both transmit and receive sides
	 * need an initialized BD list.
	 */

    FT_ASSERTVOID(instance_p->rx_bd_queue.bdring.base_bd_addr != 0);
    FT_ASSERTVOID(instance_p->tx_bd_queue.bdring.base_bd_addr != 0);
    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_RXQBASE_OFFSET,
                     instance_p->rx_bd_queue.bdring.base_bd_addr);

    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_TXQBASE_OFFSET,
                     instance_p->tx_bd_queue.bdring.base_bd_addr);

    /* clear any existed int status */
    FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET,
                     FXMAC_IXR_ALL_MASK);

    /* Enable transmitter if not already enabled */
    if ((instance_p->options & (u32)FXMAC_TRANSMITTER_ENABLE_OPTION) != 0x00000000U)
    {
        Reg = FXMAC_READREG32(instance_p->config.base_address,
                              FXMAC_NWCTRL_OFFSET);
        if ((!(Reg & FXMAC_NWCTRL_TXEN_MASK)) == TRUE)
        {
            FXMAC_WRITEREG32(instance_p->config.base_address,
                             FXMAC_NWCTRL_OFFSET,
                             Reg | (u32)FXMAC_NWCTRL_TXEN_MASK);
        }
    }

    /* Enable receiver if not already enabled */
    if ((instance_p->options & FXMAC_RECEIVER_ENABLE_OPTION) != 0x00000000U)
    {
        Reg = FXMAC_READREG32(instance_p->config.base_address,
                              FXMAC_NWCTRL_OFFSET);
        if ((!(Reg & FXMAC_NWCTRL_RXEN_MASK)) == TRUE)
        {
            FXMAC_WRITEREG32(instance_p->config.base_address,
                             FXMAC_NWCTRL_OFFSET,
                             Reg | (u32)FXMAC_NWCTRL_RXEN_MASK);
        }
    }

    /* Enable TX and RX interrupts */
    FXMAC_INT_ENABLE(instance_p, (FXMAC_IXR_TX_ERR_MASK |
                                  FXMAC_IXR_RX_ERR_MASK | (u32)FXMAC_IXR_FRAMERX_MASK |
                                  (u32)FXMAC_IXR_TXCOMPL_MASK));

    /* Enable TX Q1 Interrupts */
    FXMAC_INT_Q1ENABLE(instance_p, FXMAC_INTQ1_IXR_ALL_MASK);

    /* Mark as started */
    instance_p->is_started = FT_COMPONENT_IS_STARTED;

    return;
}

/*****************************************************************************/
/**
* Gracefully stop the Ethernet MAC as follows:
*   - Disable all interrupts from this device
*   - Stop DMA channels
*   - Disable the tansmitter and receiver
*
* Device options currently in effect are not changed.
*
* This function will disable all interrupts. Default interrupts settings that
* had been enabled will be restored when FXmacStart() is called.
*
* @param instance_p is a pointer to the instance to be worked on.
*
* @note
* This function makes use of internal resources that are shared between the
* Start, Stop, Setoptions, and Clearoptions functions. So if one task might be
* setting device options while another is trying to start the device, the user
* is required to provide protection of this shared data (typically using a
* semaphore).
*
* Stopping the DMA channels causes this function to block until the DMA
* operation is complete.
*
******************************************************************************/
void FXmacStop(FXmac *instance_p)
{
    u32 Reg;

    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY);

    /* Disable all interrupts */
    FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_IDR_OFFSET,
                     FXMAC_IXR_ALL_MASK);

    /* Disable the receiver & transmitter */
    Reg = FXMAC_READREG32(instance_p->config.base_address,
                          FXMAC_NWCTRL_OFFSET);
    Reg &= (u32)(~FXMAC_NWCTRL_RXEN_MASK);
    Reg &= (u32)(~FXMAC_NWCTRL_TXEN_MASK);
    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_NWCTRL_OFFSET, Reg);

    /* Mark as stopped */
    instance_p->is_started = 0U;
}

/*****************************************************************************/
/**
* Perform a graceful reset of the Ethernet MAC. Resets the DMA channels, the
* transmitter, and the receiver.
*
* Steps to reset
* - Stops transmit and receive channels
* - Stops DMA
* - Configure transmit and receive buffer size to default
* - Clear transmit and receive status register and counters
* - Clear all interrupt sources
* - Clear phy (if there is any previously detected) address
* - Clear MAC addresses (1-4) as well as Type IDs and hash value
*
* All options are placed in their default state. Any frames in the
* descriptor lists will remain in the lists. The side effect of doing
* this is that after a reset and following a restart of the device, frames
* were in the list before the reset may be transmitted or received.
*
* The upper layer software is responsible for re-configuring (if necessary)
* and restarting the MAC after the reset. Note also that driver statistics
* are not cleared on reset. It is up to the upper layer software to clear the
* statistics if needed.
*
* When a reset is required, the driver notifies the upper layer software of
* this need through the ErrorHandler callback and specific status codes.
* The upper layer software is responsible for calling this Reset function
* and then re-configuring the device.
*
* @param instance_p is a pointer to the instance to be worked on.
*
******************************************************************************/
void FXmacReset(FXmac *instance_p)
{
    u32 Reg;
    u8 i;
    s8 mac_addr[6] = {0};

    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY);

    /* Stop the device and reset hardware */
    FXmacStop(instance_p);
    instance_p->options = FXMAC_DEFAULT_OPTIONS;

    instance_p->max_mtu_size = FXMAC_MTU;
    instance_p->max_frame_size = FXMAC_MTU + FXMAC_HDR_SIZE +
                                 FXMAC_TRL_SIZE;
    instance_p->max_vlan_frame_size = instance_p->max_frame_size +
                                      FXMAC_HDR_VLAN_SIZE;
    instance_p->rx_buf_mask = FXMAC_RXBUF_LEN_MASK;

    /* Setup hardware with default values */
    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_NWCTRL_OFFSET,
                     (FXMAC_NWCTRL_STATCLR_MASK |
                      FXMAC_NWCTRL_MDEN_MASK) &
                         (u32)(~FXMAC_NWCTRL_LOOPEN_MASK));

    Reg = FXMAC_READREG32(instance_p->config.base_address,
                          FXMAC_NWCFG_OFFSET);
    Reg &= FXMAC_NWCFG_MDCCLKDIV_MASK;

    Reg = Reg | (u32)FXMAC_NWCFG_100_MASK |
          (u32)FXMAC_NWCFG_FDEN_MASK |
          (u32)FXMAC_NWCFG_UCASTHASHEN_MASK;

    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_NWCFG_OFFSET, Reg);

    FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_NWCFG_OFFSET,
                     (FXMAC_READREG32(instance_p->config.base_address, FXMAC_NWCFG_OFFSET) |
                      FXMAC_NWCFG_DWIDTH_64_MASK));

    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_DMACR_OFFSET,
                     (((((u32)FXMAC_RX_BUF_SIZE / (u32)FXMAC_RX_BUF_UNIT) +
                        (((((u32)FXMAC_RX_BUF_SIZE %
                            (u32)FXMAC_RX_BUF_UNIT)) != (u32)0)
                             ? 1U
                             : 0U))
                       << (u32)(FXMAC_DMACR_RXBUF_SHIFT)) &
                      (u32)(FXMAC_DMACR_RXBUF_MASK)) |
                         (u32)FXMAC_DMACR_RXSIZE_MASK |
                         (u32)FXMAC_DMACR_TXSIZE_MASK);

    FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_DMACR_OFFSET,
                     (FXMAC_READREG32(instance_p->config.base_address, FXMAC_DMACR_OFFSET) |
#if defined(__aarch64__) || defined(__arch64__)
                      (u32)FXMAC_DMACR_ADDR_WIDTH_64 |
#endif
                      (u32)FXMAC_DMACR_INCR16_AHB_AXI_BURST));

    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_TXSR_OFFSET, FXMAC_SR_ALL_MASK);

    FXmacSetQueuePtr(instance_p, 0, 0x00U, (u16)FXMAC_SEND);
    FXmacSetQueuePtr(instance_p, 0, 0x01U, (u16)FXMAC_SEND);
    FXmacSetQueuePtr(instance_p, 0, 0x00U, (u16)FXMAC_RECV);

    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_RXSR_OFFSET, FXMAC_SR_ALL_MASK);

    FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_IDR_OFFSET,
                     FXMAC_IXR_ALL_MASK);

    Reg = FXMAC_READREG32(instance_p->config.base_address,
                          FXMAC_ISR_OFFSET);
    FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET,
                     Reg);

    FXmacClearHash(instance_p);

    for (i = 0U; i < 4U; i++)
    {
        (void)FXmacGetMacAddress(instance_p, mac_addr, i);
        (void)FXmacSetMacAddress(instance_p, mac_addr, i);
        (void)FXmacSetTypeIdCheck(instance_p, 0x00000000U, i);
    }

    /* clear all counters */
    for (i = 0U; i < (u8)((FXMAC_LAST_OFFSET - FXMAC_OCTTXL_OFFSET) / 4U);
         i++)
    {
        (void)FXMAC_READREG32(instance_p->config.base_address,
                              FXMAC_OCTTXL_OFFSET + (u32)(((u32)i) * ((u32)4)));
    }

    /* Disable the receiver */
    Reg = FXMAC_READREG32(instance_p->config.base_address,
                          FXMAC_NWCTRL_OFFSET);
    Reg &= (u32)(~FXMAC_NWCTRL_RXEN_MASK);
    FXMAC_WRITEREG32(instance_p->config.base_address,
                     FXMAC_NWCTRL_OFFSET, Reg);

    /* Sync default options with hardware but leave receiver and
         * transmitter disabled. They get enabled with FXmacStart() if
	 * FXMAC_TRANSMITTER_ENABLE_OPTION and
         * FXMAC_RECEIVER_ENABLE_OPTION are set.
	 */
    (void)FXmacSetOptions(instance_p, instance_p->options &
                                          ~((u32)FXMAC_TRANSMITTER_ENABLE_OPTION |
                                            (u32)FXMAC_RECEIVER_ENABLE_OPTION));

    (void)FXmacClearOptions(instance_p, ~instance_p->options);
}

LONG FXmacCfgInitialize(FXmac *instance_p, FXmacConfig *config_p)
{
    /* Verify arguments */
    FT_ASSERTZERONUM(instance_p != NULL);
    FT_ASSERTZERONUM(config_p != NULL);

    instance_p->config = *config_p;

    /* Reset the hardware and set default options */
    instance_p->is_ready = FT_COMPONENT_IS_READY;
    FXmacResetHw(instance_p->config.base_address);
    FXmacReset(instance_p);
    return (LONG)(FT_SUCCESS);
}

/*****************************************************************************/
/**
* This function sets the start address of the transmit/receive buffer queue.
*
* @param	instance_p is a pointer to the instance to be worked on.
* @param	QPtr is the address of the Queue to be written
* @param	QueueNum is the Buffer Queue Index
* @param	Direction indicates Transmit/Receive
*
* @note
* The buffer queue addresses has to be set before starting the transfer, so
* this function has to be called in prior to FXmacStart()
*
******************************************************************************/
void FXmacSetQueuePtr(FXmac *instance_p, uintptr QPtr, u8 QueueNum,
                      u16 Direction)
{
    /* Assert bad arguments and conditions */
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY);

    /* If already started, then there is nothing to do */
    if (instance_p->is_started == (u32)FT_COMPONENT_IS_STARTED)
    {
        return;
    }

    if (QueueNum == 0x00U)
    {
        if (Direction == FXMAC_SEND)
        {
            FXMAC_WRITEREG32(instance_p->config.base_address,
                             FXMAC_TXQBASE_OFFSET,
                             (QPtr & ULONG64_LO_MASK));
        }
        else
        {
            FXMAC_WRITEREG32(instance_p->config.base_address,
                             FXMAC_RXQBASE_OFFSET,
                             (QPtr & ULONG64_LO_MASK));
        }
    }
    else
    {
        if (Direction == FXMAC_SEND)
        {
            FXMAC_WRITEREG32(instance_p->config.base_address,
                             FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, instance_p->config.tx_queue_id),
                             (QPtr & ULONG64_LO_MASK));
        }
        else
        {
            FXMAC_WRITEREG32(instance_p->config.base_address,
                             FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, instance_p->config.rx_queue_id),
                             (QPtr & ULONG64_LO_MASK));
        }
    }
#ifdef __aarch64__
    if (Direction == FXMAC_SEND)
    {
        /* Set the MSB of TX Queue start address */
        FXMAC_WRITEREG32(instance_p->config.base_address,
                         FXMAC_MSBBUF_TXQBASE_OFFSET,
                         (u32)((QPtr & ULONG64_HI_MASK) >> 32U));
    }
    else
    {
        /* Set the MSB of RX Queue start address */
        FXMAC_WRITEREG32(instance_p->config.base_address,
                         FXMAC_MSBBUF_RXQBASE_OFFSET,
                         (u32)((QPtr & ULONG64_HI_MASK) >> 32U));
    }
#endif
}